//
//  NetworkManager.m
//  Podcopter
//
//  Created by Ben Gotow on 2/4/10.
//  Copyright 2010 Apple Inc. All rights reserved.
//

#import "NetworkManager.h"

static NetworkManager *sharedNetworkManager = nil;

@implementation NetworkManager

#pragma mark Singleton Implementation

+ (NetworkManager*)sharedManager
{
    @synchronized(self) {
        if (sharedNetworkManager == nil) {
            [[self alloc] init];
        }
    }
    return sharedNetworkManager;
}
 
+ (id)allocWithZone:(NSZone *)zone
{
    @synchronized(self) {
        if (sharedNetworkManager == nil) {
            sharedNetworkManager = [super allocWithZone:zone];
            return sharedNetworkManager;
        }
    }
    return nil;
}
 
- (id)copyWithZone:(NSZone *)zone
{
    return self;
}
 
- (id)retain
{
    return self;
}
 
- (unsigned)retainCount
{
    //denotes an object that cannot be released
    return UINT_MAX;
}
 
- (void)release
{
    //do nothing
}
 
- (id)autorelease
{
    return self;
}

- (id)init
{
    if (self = [super init]){
        // Determine the name of the device, and use it as our name on the network.
		name = [[[UIDevice currentDevice] name] retain];
        plist = [[[UIDevice currentDevice] uniqueIdentifier] retain];
        visibleServers = [[NSMutableDictionary alloc] init];
    
        
        // Create a reachability object that keeps us updated about the network status.
        reachability = [[NetReachability alloc] initWithDefaultRoute: NO];
        [reachability setDelegate: self];
    }
    return self;
}

- (GameClient*)client
{
    return client;
}

- (GameServer*)server
{
    return server;
}

- (BOOL) reachable
{
    return ([reachability isReachable]);
}

- (NSString*)networkName
{
    return name;
}

- (id)networkPlist
{
    return plist;
}

- (NSArray *)visibleServersArray
{
    return [visibleServers allValues];
}

- (void)dealloc
{
    [reachability release];
    [server release];
    [client release];
    [super dealloc];    
}

/* This function is called when our reachability object tells us the network has been reset. */
- (void) reachabilityDidUpdate:(NetReachability*)reachability reachable:(BOOL)reachable usingCell:(BOOL)usingCell
{
    if (clientOn || serverOn){
        if ((reachable == NO) || (usingCell == YES)){
            [[NSNotificationCenter defaultCenter] postNotificationName:@"NMNetworkLost" object:nil];
            automaticallyReenableClient = clientOn;
            automaticallyReenableServer = serverOn;
            [self commitNetworkingDisable];
        }
    } else {
        if ((reachable == YES) || (usingCell == NO)){
            if (automaticallyReenableClient)
                [self startClientSearch];
            if (automaticallyReenableServer)
                [self startServer];
            automaticallyReenableClient = NO;
            automaticallyReenableServer = NO;
        }
    }
}

#pragma mark Turning it on!

- (void)startClientSearch
{
    BOOL on = (server != nil);

    if (on)
        return;
        
    if (([reachability isUsingCell] == NO) && ([reachability isReachable] == YES)){
        [plist release];
        plist = [[[UIDevice currentDevice] uniqueIdentifier] retain];
        
        on = YES;
        
        // Create the client instance to find other sources when requested.
        client = [[GameClient alloc] init];
        
        // Begin the bonjour update cycle.
        [client setDelegate: self];

        if (![client startDiscoveringServersWithIdentifier: kGameIdentifier]){
            UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Critical Problem" message:@"The network manager could begin the server discovery process. An unknown error occurred."
                                                    delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil];

            [alert show];
            [alert release];
            on = NO;
        }
    }
    clientOn = on;
}

- (void)startServer
{
    BOOL on = (server != nil);

    if (on)
        return;

    if (([reachability isUsingCell] == NO) && ([reachability isReachable] == YES)){
        [plist release];
        plist = [[[UIDevice currentDevice] uniqueIdentifier] retain];
        
        on = YES;
        
        // Create the server instance that will announce our drawings.
        // The server talks to us to get information from the localDrawingSource.
       
        server = [[GameServer alloc] initWithPort:kServerPort];
        [server setDelegate:self];
       
       if (![server startAdvertisingToClientsWithIdentifier:kGameIdentifier]){
            UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Critical Problem" message:@"The network manager could not broadcast on bonjour."
                                                      delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil];

            [alert show];
            [alert release];
            on = NO;
        }
    }
    serverOn = on;
}

- (void)commitNetworkingDisable
{
    if (server == nil)
        return;
        
    [server stopAdvertisingToClients];
    [server release];
    server = nil;
    
    [client stopDiscoveringServers];
    [client release];
    client = nil;
    
    clientOn = NO;
    serverOn = NO;
    
    [visibleServers removeAllObjects];
}

#pragma mark Game Client Delegate Functions

- (void) gameClientDidStartDiscoveringServers:(GameClient*)c
{

}

- (void) gameClientDidUpdateOnlineServers:(GameClient*)c
{
    NSArray*                    array = [c onlineServers];
    GamePeer*                   peer;
    
    // We take an interesting approach here. We don't want to iterate through the existing dictionary
    // and try to determine if the peers still exist / are new, etc... So we create a new dictionary
    // and fill it with network member objects drawn from the old dictionary - and new ones if they
    // didn't exist. Then we just release the old dictionary and all the old members are automatically
    // destroyed. That should give us minimal overhead in most scenarios.
    
    [visibleServers removeAllObjects];
	for (peer in array){
        NSString * remoteAddress = [peer addressWithoutPort];
        [visibleServers setObject:peer forKey: remoteAddress];
    }
    
    [[NSNotificationCenter defaultCenter] postNotificationName:@"ServersUpdated" object:visibleServers];
}

- (void) gameClientWillStopDiscoveringServers:(GameClient*)c
{

}

#pragma mark Game Server Delegate Functions

- (void) gameServer:(GameServer*)server gamePeerDidConnect:(GamePeer*)peer
{
    // do something with connection
    [[NSNotificationCenter defaultCenter] postNotificationName: @"PeerConnected" object: peer];
}

- (void) gameServer:(GameServer *)server gamePeerDidDisconnect:(GamePeer *)peer
{
    // do something with connection
    [[NSNotificationCenter defaultCenter] postNotificationName: @"PeerDisconnected" object: peer];
}

- (void) gameServerDidStartAdvertisingToClients:(GameServer*)server
{
}

- (void) gameServerWillStopAdvertisingToClients:(GameServer*)server
{
}


@end
